/**
 * 2017:created by jordia
 * 20220304:[require] to [import]...jordia
 * 20221018: restructuring with typescript, major alteration.
 */
 import {OrthographicCamera,Matrix4,Camera,Object3D} from 'three';
 import {Utils} from '../general/utils';
 

 /**
  * cabinet camera
  */
 export default class CabinetCamera extends OrthographicCamera{

    public readonly isCabinetCamera=true;
    public angle:number;
    public focusLength:number;
    public cabinetMatrix:Matrix4;

     constructor(left?:number,right?:number,top?:number,bottom?:number,near?:number,far?:number,angle?:number,focusLength?:number){
         super(left,right,top,bottom,near,far);
         this.angle=angle===undefined?Math.PI/4:angle;
         this.focusLength=focusLength===undefined?10:focusLength;
         this.cabinetMatrix=new Matrix4();
         this.updateProjectionMatrix();
     }
     public copy(source:this):this {
         Camera.prototype.copy.call(this, source);
         this.left = source.left;
         this.right = source.right;
         this.top = source.top;
         this.bottom = source.bottom;
         this.near = source.near;
         this.far = source.far;
         this.zoom = source.zoom;
         this.angle = source.angle;
         this.focusLength = source.focusLength;
         this.cabinetMatrix=source.cabinetMatrix.clone();
         this.view = source.view === null ? null : Object.assign({}, source.view);
         return this;
     }
     public clone():this{
         const c=new CabinetCamera();
         c.copy(this);
         return c as this;
     }
     public setViewOffset(fullWidth:number, fullHeight:number, x:number, y:number, width:number, height:number) {
         this.view = {
             enabled:true,
             fullWidth: fullWidth,
             fullHeight: fullHeight,
             offsetX: x,
             offsetY: y,
             width: width,
             height: height
         };
         this.updateProjectionMatrix();
     }
     public clearViewOffset():void {
        if ( this.view !== null ) {
			this.view.enabled = false;
		}
         this.updateProjectionMatrix();
     }
     public updateProjectionMatrix():void {
         const dx = ( this.right - this.left ) / ( 2 * this.zoom );
         const dy = ( this.top - this.bottom ) / ( 2 * this.zoom );
         const cx = ( this.right + this.left ) / 2;
         const cy = ( this.top + this.bottom ) / 2;
         let left = cx - dx;
         let right = cx + dx;
         let top = cy + dy;
         let bottom = cy - dy;
         if (this.view !== null && this.view.enabled) {
             const zoomW = this.zoom / ( this.view.width / this.view.fullWidth );
             const zoomH = this.zoom / ( this.view.height / this.view.fullHeight );
             const scaleW = ( this.right - this.left ) / this.view.width;
             const scaleH = ( this.top - this.bottom ) / this.view.height;
             left += scaleW * ( this.view.offsetX / zoomW );
             right = left + scaleW * ( this.view.width / zoomW );
             top -= scaleH * ( this.view.offsetY / zoomH );
             bottom = top - scaleH * ( this.view.height / zoomH );
         }
         this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far);
         const offset = Math.sin(Math.PI / 4) * this.focusLength / 2;
         this.cabinetMatrix=Utils.makeCabinetShearMatrix(this.angle, -offset, -offset, 0).clone();
         this.projectionMatrix.multiply(this.cabinetMatrix);
     }
     public toJSON(meta:any):any {
         const data = Object3D.prototype.toJSON.call(this, meta);
         data.object.zoom = this.zoom;
         data.object.left = this.left;
         data.object.right = this.right;
         data.object.top = this.top;
         data.object.bottom = this.bottom;
         data.object.near = this.near;
         data.object.far = this.far;
         data.object.angle = this.angle;
         data.object.focusLength = this.focusLength;
         data.object.cabinetMatrix=this.cabinetMatrix.clone();
         if (this.view !== null) data.object.view = Object.assign({}, this.view);
         return data;
     }
 }